package orchestration.persistence.sqlite;

import java.io.Serializable;
import java.util.List;

import orchestration.android.parcelable.ParcelableArtifact;
import orchestration.android.parcelable.ParcelableProperty;
import orchestration.businessprocess.execution.Property;
import orchestration.businessprocess.model.BPArtifact;
import orchestration.persistence.PropertyDAO;
import orchestration.persistence.OrchestratorAuthority.AndroidProcessExecutionProperty;
import orchestration.persistence.exceptions.ConversionException;
import orchestration.persistence.exceptions.OrchestrationDAOException;
import orchestration.utils.ConversionUtils;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;

/**
 * @author  <a href="mailto:car.ferrarini@gmail.com">Carlo Ferrarini</a>
 */
public class PropertyDAOImpl implements PropertyDAO{

	/**
	 * @uml.property  name="dbHelper"
	 * @uml.associationEnd  
	 */
	private OrchestratorDatabaseHelper dbHelper;

	public PropertyDAOImpl(OrchestratorDatabaseHelper dbHelper) {
		this.dbHelper= dbHelper;
	}

	@Override
	public void deleteProperties4BPExecution(long procExecutionId) {
		dbHelper.getWritableDatabase().delete(OrchestratorDatabaseHelper.TABLE_PROPERTIES,  AndroidProcessExecutionProperty.EXECUTION_REF+"=?", ConversionUtils.long2StringArray(procExecutionId));

	}

	@Override
	public  Property getProperty(long processExecutionId,
			BPArtifact artifact) throws OrchestrationDAOException {
		if(artifact==null)
			throw new OrchestrationDAOException("cannot retrieve a Property with null artifact");
		//prepare whereClause
		String whereClause = AndroidProcessExecutionProperty.EXECUTION_REF+"=? AND "+ AndroidProcessExecutionProperty.ARTIFACT_REF+"=?";
		//prepare where args
		String[] whereArgs= new String[2];
		whereArgs[0]=""+processExecutionId;
		whereArgs[1]=""+artifact.getId().getValue();
		//retrieve data
		Cursor cursor = dbHelper.getReadableDatabase().query(OrchestratorDatabaseHelper.TABLE_PROPERTIES,
				OrchestratorDatabaseHelper.PROPERTY_FIELDS,
				whereClause,whereArgs, null, null, null);
		cursor.moveToFirst();
		if(cursor.getCount()!=1)
			return null;
		long id = cursor.getLong(cursor.getColumnIndex(AndroidProcessExecutionProperty._ID));
		byte[] valueBytes= cursor.getBlob(cursor.getColumnIndex(AndroidProcessExecutionProperty.VALUE));
		cursor.close();
		Object value;
		try {
			value = ConversionUtils.byteArray2Object(valueBytes);
			//create and return the property
			Property result= new ParcelableProperty((ParcelableArtifact)artifact);
			Class<?> type = result.getType();
			if(type.equals(Uri.class)){
				String stringVal = (String)value;
				value= stringVal==null?null:Uri.parse(stringVal);
			}else if(type.isArray() && type.getComponentType().equals(Uri.class)){
				value = ConversionUtils.stringArray2UriArray((String[])value);
			}else if(List.class.isAssignableFrom(type) && result.getComponentType().equals(Uri.class)){
				value= ConversionUtils.stringList2UriList((List<String>)value);
			}
			result.setId(id);
			result.setValue(value);
			return result;
		} catch (ConversionException e) {
			throw new OrchestrationDAOException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void saveProperty(Property property,
			long procExecutionId) throws OrchestrationDAOException {
		Serializable serializableVal;
		Object value = property.getValue();
		Class<?> type = property.getType();
		if(value==null)
			serializableVal=null;
		else if(type.equals(Uri.class) && value!=null)
			serializableVal= ((Uri)value).toString();
		else if(type.isArray() && type.getComponentType().equals(Uri.class))
			serializableVal=ConversionUtils.uriArray2StringArray((Uri[])value);
		else if(List.class.isAssignableFrom(type) && property.getComponentType().equals(Uri.class))
			serializableVal=(Serializable)ConversionUtils.uriList2StringList((List<Uri>)value);
		else
			serializableVal=(Serializable)value;
		byte[] valueBytes;
		try {
			valueBytes = ConversionUtils.object2ByteArray(serializableVal);
			ContentValues values= new ContentValues(3);
			values.put(AndroidProcessExecutionProperty.EXECUTION_REF, procExecutionId);
			values.put(AndroidProcessExecutionProperty.ARTIFACT_REF, property.getArtifactId().getValue());
			values.put(AndroidProcessExecutionProperty.VALUE, valueBytes);
			long id = property.getId();
			if(id==0L){
				id= insertProperty(values);
				property.setId(id);
			}
			else
			updateProperty(values,id);
		} catch (ConversionException e) {
			throw new OrchestrationDAOException(e);
		}
	}

	private void updateProperty(ContentValues values, long id) {
		String[] whereArgs = ConversionUtils.long2StringArray(id);
		String whereClause=AndroidProcessExecutionProperty._ID+ "=?";
		dbHelper.getWritableDatabase().update(OrchestratorDatabaseHelper.TABLE_PROPERTIES, values, whereClause, whereArgs);
	}

	private long insertProperty(ContentValues values) throws OrchestrationDAOException {
		long id= dbHelper.getWritableDatabase().insert(OrchestratorDatabaseHelper.TABLE_PROPERTIES,AndroidProcessExecutionProperty.ARTIFACT_REF, values);
		if(id==-1)
			throw new OrchestrationDAOException("Error occurred while inserting the new property");
		return id;
	}
}
